package entity

import (
	"errors"
	"fmt"
	"src/gcommon"
	"src/zeus/iserver"
	"src/zeus/msgdef"
	"src/zeus/pool"
	"src/zeus/serializer"
	"src/zeus/sess"

	"src/zeus/logger"
)

// PostRaw 投递一个原始消息
func (e *Entity) PostRaw(srvType uint8, rawMsg []byte) error {
	// if srvType == iserver.ServerTypeClient || srvType == iserver.ServerTypeGateway {
	// 	return nil
	// }

	// if e.GetEntityState() != iserver.Entity_State_Loop {
	// 	return fmt.Errorf("error entity state %d ", e.GetEntityState())
	// }

	if rawMsg == nil {
		return fmt.Errorf("Messsage is nil")
	}
	// 如果投递给客户端且本地就有客户端连接话，直接投递
	if srvType == iserver.ServerTypeClient {
		if sess := e.GetClientSess(); sess != nil {
			sess.SendRaw(rawMsg)
			return nil
		} else if e.isDirectMode.Load() {
			return nil
		}
	}
	srvID, spaceID, err := e.GetEntitySrvID(srvType)
	if err != nil {
		return err
	}

	if srvID == iserver.GetSrvInst().GetSrvID() {
		//正常应该不会进来
		return nil
	}

	var packMsg msgdef.IMsg
	if packMsg, err = e.packRawMsg(srvType, spaceID, rawMsg); err != nil {
		return err
	}

	return iserver.GetSrvInst().PostMsgToSrv(srvID, packMsg)

}

// PostRawToSpace 投递一个原始消息给它的SpaceEntity部分
func (e *Entity) PostRawToSpace(rawMsg []byte) error {
	if e.spaceSrvType == 0 {
		return errors.New("not exist space part")
	}

	return e.PostRaw(e.spaceSrvType, rawMsg)
}

// Post 投递一个消息给指定的部分
// 发送消息可能失败，在极端情况下
func (e *Entity) Post(srvType uint8, msg msgdef.IMsg) error {
	if msg == nil {
		return fmt.Errorf("Message is nil")
	}

	//如果投递给客户端且本地就有客户端连接话，直接投递
	if srvType == iserver.ServerTypeClient {
		if sess := e.GetClientSess(); sess != nil {
			sess.Send(msg)
			return nil
		} else if e.isDirectMode.Load() {
			return nil
		}
	}

	srvID, spaceID, err := e.GetEntitySrvID(srvType)
	if err != nil {
		return err
	}

	//如果是投递给自己的消息，直接处理
	if srvID == iserver.GetSrvInst().GetSrvID() {
		e.MsgHandler.FireMsg(msg.Name(), msg)
		return nil
	}

	var packMsg msgdef.IMsg
	if packMsg, err = e.packMsg(srvType, spaceID, msg); err != nil {
		return err
	}

	return iserver.GetSrvInst().PostMsgToSrv(srvID, packMsg)
}

// DelayPost 延迟发送消息, 在帧尾发送
func (e *Entity) DelayPost(srvType uint8, msg msgdef.IMsg) error {
	if msg == nil {
		return fmt.Errorf("Message is nil")
	}
	if tmp, ok := e.realPtr.(iserver.IDelayCallHost); ok {
		tmp.CallDelay(func() {
			e.Post(srvType, msg)
		})
	} else {
		e.GetEntities().CallDelay(func() {
			e.Post(srvType, msg)
		})
	}

	// e.delayedMsgs = append(e.delayedMsgs, &delayedSendMsg{
	// 	srvType: srvType,
	// 	msg:     msg,
	// })

	return nil
}

// 发送所有延迟发送的消息
func (e *Entity) FlushDelayedMsgs() {
	// if len(e.delayedMsgs) == 0 {
	// 	return
	// }
	// for _, dm := range e.delayedMsgs {
	// 	e.Post(dm.srvType, dm.msg)
	// }
	// e.delayedMsgs = e.delayedMsgs[0:0]
}

// PostToSpace 投递一个消息给它的SpaceEntity部分
func (e *Entity) PostToSpace(msg msgdef.IMsg) error {
	if e.spaceSrvType == 0 {
		return fmt.Errorf("not exist space part")
	}

	return e.Post(e.spaceSrvType, msg)
}

// RPC 快速消息调用
func (e *Entity) RPC(srvType uint8, methodName string, args ...interface{}) error {
	data := serializer.Serialize(args...)
	msg := &msgdef.RPCMsg{}
	msg.ServerType = srvType
	msg.SrcEntityID = e.entityID
	msg.MethodName = methodName
	msg.Data = data

	return e.DelayPost(srvType, msg)
}

// RPCOther 快速触发一个其它实体的方法
/*func (e *Entity) RPCOther(srvType uint8, srcEntityID uint64, methodName string, args ...interface{}) error {
	data := serializer.Serialize(args...)
	msg := &msgdef.RPCMsg{}
	msg.ServerType = srvType
	msg.SrcEntityID = srcEntityID
	msg.MethodName = methodName
	msg.Data = data

	return e.DelayPost(srvType, msg)
}*/

// SpaceRPC 快速调用spaceentity的rpc方法
func (e *Entity) SpaceRPC(methodName string, args ...interface{}) error {
	if e.spaceSrvType == 0 {
		return fmt.Errorf("not exist space part")
	}
	return e.RPC(e.spaceSrvType, methodName, args)
}

func (e *Entity) packMsg(srvType uint8, spaceID uint64, msg msgdef.IMsg) (msgdef.IMsg, error) {
	return PackMsg(srvType, e.entityID, spaceID, msg)
}

func PackMsg(srvType uint8, entityID, spaceID uint64, msg msgdef.IMsg) (msgdef.IMsg, error) {
	//buf := make([]byte, sess.MaxMsgBuffer)
	buf := pool.Get(msg.Size() + sess.MsgHeadSize)
	defer pool.Put(buf)
	var encBuf []byte
	var err error

	if gcommon.GetCfgInst().Config.EncryptEnabled && srvType == iserver.ServerTypeClient {
		encBuf, err = sess.EncodeMsgWithEncrypt(msg, buf, true, true)
	} else {
		encBuf, err = sess.EncodeMsg(msg, buf, true)
	}
	if err != nil {
		return nil, err
	}

	msgContent := make([]byte, len(encBuf))
	copy(msgContent, encBuf)

	return &msgdef.EntityMsgTransport{
		SrvType:    srvType,
		SpaceID:    spaceID,
		EntityID:   entityID,
		MsgContent: msgContent,
	}, nil
}

func (e *Entity) packRawMsg(srvType uint8, spaceID uint64, rawMsg []byte) (msgdef.IMsg, error) {

	return &msgdef.EntityMsgTransport{
		SrvType:    srvType,
		EntityID:   e.entityID,
		SpaceID:    spaceID,
		MsgContent: rawMsg,
	}, nil
}

// 生成消息的二进制
func PackRaw(msg msgdef.IMsg) (result []byte, err error) {
	result = make([]byte, msg.Size()+sess.MsgHeadSize)
	if gcommon.GetCfgInst().Config.EncryptEnabled {
		_, err = sess.EncodeMsgWithEncrypt(msg, result, true, true)
	} else {
		_, err = sess.EncodeMsg(msg, result, true)
	}
	return
}

/////////////////////////////////////////////////////////////////////////////
// 此处为Entity相关消息
////////////////////////////////////////////////////////////////////////////

// MsgProc_PropsSync 实体间同步数据的消息
func (e *Entity) MsgProc_PropsSync(msg *msgdef.PropsSync) {
	if msg.Num > 100 {
		logger.Error("prop num is exceed 100 , is right ?")
		return
	}

	e.realPtr.ReflushFromMsg(int(msg.Num), msg.Data)
}

// MsgProc_RPCMsg 实体间RPC调用消息处理
func (e *Entity) MsgProc_RPCMsg(msg *msgdef.RPCMsg) {
	// 从客户端收到的消息要判断要调用的服务器类型
	// logger.Debug("Entity.RPCMsg:", msg.MethodName)
	if (msg.ServerType == iserver.ServerTypeSpace &&
		e.GetSpaceID() != 0) ||
		msg.ServerType == iserver.GetSrvInst().GetSrvType() {
		//是给场景服发的消息，需要知道自己是不是场景服
		if msg.SrcEntityID == e.GetID() || msg.SrcEntityID == 0 {
			e.FireRPC(msg.MethodName, msg.Data)
		} else {
			iE, ok := e.GetRealPtr().(iserver.IEntrustedRPCCaller)
			if ok {
				iE.EntrustedRPC(msg)
			}
		}
	} else {
		e.Post(msg.ServerType, msg)
	}
}
